package com.zn.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.zn.entity.*;
import com.zn.mapper.*;
import com.zn.service.DocumentaryTradeService;
import com.zn.socket.SocketHandler;
import com.zn.task.MockTradeTask;
import com.zn.util.*;
import com.zn.vo.Message;
import com.zn.vo.OptionSymbol;
import com.zn.vo.Response;
import com.zn.web.dto.OptionTradeDto;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author YinJiaWei
 * @version 2019/2/26 16:26
 */
@Service
public class DocumentaryTradeServiceImpl extends BaseServiceImpl implements DocumentaryTradeService {
    private static final Logger LOGGER = LoggerFactory.getLogger(DocumentaryTradeServiceImpl.class);
    @Autowired
    private AppCustomerMapper appCustomerMapper;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private DocumentaryReportOrderMapper documentaryReportOrderMapper;
    @Autowired
    private DocumentaryCloseOrderMapper documentaryCloseOrderMapper;
    @Autowired
    private DocumentaryOpenOrderMapper documentaryOpenOrderMapper;
    @Autowired
    private OptionTradingGoodsMapper optionTradingGoodsMapper;
    @Autowired
    private CSocketInfo socketInfo;
    @Autowired
    private OptionAccountMapper optionAccountMapper;
    @Autowired
    private MockTradeTask mockTradeTask;

    /**
     * 开仓跟单报单请求
     * @param futureReportOrder
     */
    @Override
    public void reqsOpenDocumentaryReportOrder(FutureReportOrder futureReportOrder) {
        try {
            AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(futureReportOrder.getCustomerId());
            String direction="";
            if(appCustomer.getDocumentaryStatus()==1){
                /**正向跟单**/
                direction=futureReportOrder.getDirection();
            }else if(appCustomer.getDocumentaryStatus()==2){
                /**反向跟单 0-买 1-卖**/
                direction=futureReportOrder.getDirection().equals("0")?"1":"0";
            }else{
                LOGGER.error(appCustomer.getCustomerTradeAccount()+"AppCustomer documentaryStatus Error............"+appCustomer.getDocumentaryStatus());
                return ;
            }
            /**查询启用的交易账户**/
            OptionAccount optionAccount=optionAccountMapper.selectAccountByStatus(0,2);
            /**交易账户信息**/
            if(optionAccount==null){
                /**交易账户异常**/
                LOGGER.error("optionAccount is null----------> order_ref:"+futureReportOrder.getOrderRef());
                return;
            }
            String orderRef=getOrderRefStr();
            //拼接委托对象数据
            DocumentaryReportOrder documentaryReportOrder=new DocumentaryReportOrder();
            documentaryReportOrder.setReportId(UUIDUtils.getUuid());
            documentaryReportOrder.setCustomerId(futureReportOrder.getCustomerId());
            documentaryReportOrder.setInvestorId(optionAccount.getOptionTradeAccount());
            documentaryReportOrder.setInstrumentCode(futureReportOrder.getInstrumentCode());
            documentaryReportOrder.setInstrumentName(futureReportOrder.getInstrumentName());
            documentaryReportOrder.setProductCode(futureReportOrder.getProductCode());
            documentaryReportOrder.setProductName(futureReportOrder.getProductName());
            documentaryReportOrder.setOrderRef(orderRef);
            documentaryReportOrder.setReportOrderRef(futureReportOrder.getOrderRef());
            documentaryReportOrder.setLimitPrice(futureReportOrder.getLimitPrice());
            documentaryReportOrder.setDirection(direction);
            documentaryReportOrder.setOffsetFlag(futureReportOrder.getOffsetFlag());
            documentaryReportOrder.setVolume(futureReportOrder.getVolume());
            /**补充报单价格类型**/
            documentaryReportOrder.setOrderPriceType(futureReportOrder.getOrderPriceType());
            //futureReportOrder.setStopLossPoint(new BigDecimal(futuresTradeDto.getStopLossPoint()).setScale(3, BigDecimal.ROUND_HALF_UP));
            //下单止损金额
            //futureReportOrder.setStopLossAmount(new BigDecimal(stopLossAmount).setScale(3, BigDecimal.ROUND_HALF_UP));
            //下单总保证金
            documentaryReportOrder.setFrozenDeposit(futureReportOrder.getFrozenDeposit());
            //总开仓平仓手续费
            documentaryReportOrder.setTotalFee(futureReportOrder.getTotalFee());
            /**开仓手续费单价**/
            documentaryReportOrder.setUnivalentOpenFee(futureReportOrder.getUnivalentOpenFee());
            /**平仓手续费单价**/
            documentaryReportOrder.setUnivalentCloseFee(futureReportOrder.getUnivalentCloseFee());
            /**开仓成本手续费单价**/
            documentaryReportOrder.setUnivalentCostOpenFee(futureReportOrder.getUnivalentCostCloseFee());
            /**平仓成本手续费单价**/
            documentaryReportOrder.setUnivalentCostCloseFee(futureReportOrder.getUnivalentCostCloseFee());
            documentaryReportOrder.setCostTotalFee(futureReportOrder.getCostTotalFee());
            documentaryReportOrderMapper.insertSelective(documentaryReportOrder);

            //拼接tcp/ip传输数据串 客户交易账号|母账户|合约code|本地报单编号|价格|手数|多空方向|开平方向|止损点位|订单报单类型（0-用户报单，1-对冲单，2-跟单）|报单价格类型（0-市价 1-限价）
            //reqorder|dev10001001|5321101611|10001588|dev190226100004|0.0055|2|0|0|0|1|1
            String dataStr="reqorder|"+appCustomer.getCustomerTradeAccount()+"|"+optionAccount.getOptionTradeAccount()+"|"+futureReportOrder.getInstrumentCode()+
                    "|"+orderRef+"|"+futureReportOrder.getLimitPrice()+"|"+futureReportOrder.getVolume()+"|"+direction+"|"
                    +futureReportOrder.getOffsetFlag()+"|0|"+Constant.DOCUMENTARY_ORDER_FLAG+"|"+futureReportOrder.getOrderPriceType();
            SocketHandler socketHandler= null;
            LOGGER.info("reqsOpenDocumentaryReportOrder----->dataStr------------------->"+dataStr);
            /**记录次条报单是由那台机器发出的**/
            redisTemplate.opsForValue().set(Constant.ORDER_REF+documentaryReportOrder.getOrderRef(), LocalInetAddress.getLocalAddress(),12, TimeUnit.HOURS);
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            boolean flag=socketHandler.isServerClose();
            if(flag){
                throw new Exception("socket连接断开异常");
            }
//            LOGGER.info("dataStr:" + dataStr);
            socketHandler.sendMessage(dataStr);
//            mockTradeTask.mockDocuReportOrder(this,dataStr);
        }catch (Exception e){
            LOGGER.error("reqsOpenDocumentaryReportOrder error--->", e);
            //手动回滚数据
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    @Override
    public void reqsCloseDocumentaryReportOrder(FutureReportOrder futureReportOrder,String reportOpenOrderRef,Integer documentaryStatus){
        LOGGER.info("reqsCloseDocumentaryReportOrder--->reportOpenOrderRef:"+reportOpenOrderRef+"|reportCloseOrderRef:"+futureReportOrder.getOrderRef());
        AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(futureReportOrder.getCustomerId());
        String direction="";
        if(documentaryStatus==1){
            /**正向跟单**/
            direction=futureReportOrder.getDirection();
        }else if(documentaryStatus==2){
            /**反向跟单 0-买 1-卖**/
            direction=futureReportOrder.getDirection().equals("0")?"1":"0";
        }else{
            LOGGER.error(appCustomer.getCustomerTradeAccount()+"AppCustomer documentaryStatus Error............"+documentaryStatus);
            return ;
        }
        /**平仓对应的开仓报单信息**/
        DocumentaryReportOrder documentaryOpenReportOrder=documentaryReportOrderMapper.selectByReportOrderRef(reportOpenOrderRef);
        if(documentaryOpenReportOrder==null){
            LOGGER.error("reqsCloseDocumentaryReportOrder--->documentaryReportOrder--->is Null"+reportOpenOrderRef);
            return;
        }
        //校验该用户是否有平仓手数
        List<DocumentaryOpenOrder> documentaryOpenOrderList=documentaryOpenOrderMapper.selectListByOrderRef(documentaryOpenReportOrder.getOrderRef());
        if(documentaryOpenOrderList.isEmpty()){
            LOGGER.error("reqsCloseDocumentaryReportOrder--->documentaryOpenOrderList--->is Null"+documentaryOpenReportOrder.getOrderRef());
            return;
        }
        String optionAccount=documentaryOpenOrderList.get(0).getInvestorId();
        String openOrderRef=documentaryOpenOrderList.get(0).getOrderRef();
        Integer effectiveHoldVolume=0;
        for(DocumentaryOpenOrder obj:documentaryOpenOrderList){
            effectiveHoldVolume=effectiveHoldVolume+obj.getEffectiveHoldVolume();
        }
        Integer closeVolume=futureReportOrder.getVolume();
        //可用手数<平仓手数 则把可用全部平掉
        if(effectiveHoldVolume<futureReportOrder.getVolume()){
            closeVolume=effectiveHoldVolume;
        }
        if(closeVolume<=0){
            LOGGER.error("reqsCloseDocumentaryReportOrder--->closeVolume is 0--->"+futureReportOrder.getOrderRef()+"|"+closeVolume);
            return;
        }
        //拼接委托对象数据
        DocumentaryReportOrder documentaryReportOrder=new DocumentaryReportOrder();
        documentaryReportOrder.setReportId(UUIDUtils.getUuid());
        documentaryReportOrder.setCustomerId(futureReportOrder.getCustomerId());
        documentaryReportOrder.setInvestorId(optionAccount);
        documentaryReportOrder.setProductCode(futureReportOrder.getProductCode());
        documentaryReportOrder.setProductName(futureReportOrder.getProductName());
        documentaryReportOrder.setInstrumentCode(futureReportOrder.getInstrumentCode());
        documentaryReportOrder.setInstrumentName(futureReportOrder.getInstrumentName());
        documentaryReportOrder.setOrderRef(getOrderRefStr());
        documentaryReportOrder.setUnivalentOpenFee(futureReportOrder.getUnivalentOpenFee());
        documentaryReportOrder.setUnivalentCloseFee(futureReportOrder.getUnivalentCloseFee());
        documentaryReportOrder.setUnivalentCostOpenFee(futureReportOrder.getUnivalentCostOpenFee());
        documentaryReportOrder.setUnivalentCostCloseFee(futureReportOrder.getUnivalentCostCloseFee());
        documentaryReportOrder.setLimitPrice(futureReportOrder.getLimitPrice());
        documentaryReportOrder.setDirection(direction);
        documentaryReportOrder.setOffsetFlag(futureReportOrder.getOffsetFlag());
        documentaryReportOrder.setVolume(closeVolume);
        documentaryReportOrder.setCreateTime(new Date());
        documentaryReportOrder.setUpdateTime(new Date());
        /**补充报单价格类型**/
        documentaryReportOrder.setOrderPriceType(futureReportOrder.getOrderPriceType());
        /**补充对应的用户报单编号**/
        documentaryReportOrder.setReportOrderRef(futureReportOrder.getOrderRef());
        //平仓报单录入数据
        documentaryReportOrderMapper.insertSelective(documentaryReportOrder);
        //递归去扣可用手数
        this.buckleEffectiveHoldVolume(openOrderRef, futureReportOrder.getVolume());
        //修改成功 保存平仓时的部分数据
        DocumentaryCloseOrder documentaryCloseOrder=new DocumentaryCloseOrder();
        documentaryCloseOrder.setCloseId(UUIDUtils.getUuid());
        documentaryCloseOrder.setOpenOrderRef(openOrderRef);
        documentaryCloseOrder.setCustomerId(documentaryReportOrder.getCustomerId());
        documentaryCloseOrder.setInvestorId(optionAccount);
        documentaryCloseOrder.setProductCode(futureReportOrder.getProductCode());
        documentaryCloseOrder.setProductName(futureReportOrder.getProductName());
        documentaryCloseOrder.setInstrumentCode(futureReportOrder.getInstrumentCode());
        documentaryCloseOrder.setInstrumentName(futureReportOrder.getInstrumentName());
        documentaryCloseOrder.setReportId(documentaryReportOrder.getReportId());
        /**暂时不给开仓价**/
//        futureCloseOrder.setOpenPrice(futureOpenOrder.getPrice());
        documentaryCloseOrder.setStatus(0);
        documentaryCloseOrder.setCreateTime(new Date());
        documentaryCloseOrder.setUpdateTime(new Date());
        documentaryCloseOrderMapper.insertSelective(documentaryCloseOrder);
        //拼接tcp/ip传输数据串 reqorder|客户交易账号|母账户|合约code|本地报单编号|价格|手数|多空方向|开平方向|开仓本地报单编号|订单报单类型（0-用户普通报单，1-对冲单，2-跟单）|报单价格类型（0-市价 1-限价）
        String dataStr="reqorder|"+appCustomer.getCustomerTradeAccount()+"|" +optionAccount +"|"+futureReportOrder.getInstrumentCode()+"|"+documentaryReportOrder.getOrderRef()+
                "|"+futureReportOrder.getLimitPrice()+"|"+closeVolume+
                "|"+direction+"|"+futureReportOrder.getOffsetFlag()+"|"+documentaryOpenOrderList.get(0).getOrderRef()+"|"+Constant.DOCUMENTARY_ORDER_FLAG+"|"+futureReportOrder.getOrderPriceType();

        SocketHandler socketHandler= null;
        LOGGER.info("reqsCloseDocumentaryReportOrder-->dataStr:"+dataStr);
        try {
            /**记录次条报单是由那台机器发出的 60s后自动删除**/
            redisTemplate.opsForValue().set(Constant.ORDER_REF+documentaryReportOrder.getOrderRef(),LocalInetAddress.getLocalAddress(),12,TimeUnit.HOURS);
            socketHandler = SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            /**判断是否断开连接，断开返回true,没有返回false**/
            boolean flag=socketHandler.isServerClose();
            if(flag){
                throw new Exception("socket连接断开异常");
            }
//            LOGGER.info("reqsCloseDocumentaryReportOrder -->" + dataStr);
            socketHandler.sendMessage(dataStr);
//            mockTradeTask.mockDocuReportOrder(this, dataStr);
        } catch (Exception e) {
            LOGGER.error("reqsCloseReportOrder connection socket err ....." , e);
            //手动回滚数据
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    @Override
    public void reqsCancelDocumentaryReportOrder(String reportOrderRef){
        try {
            DocumentaryReportOrder documentaryReportOrder=documentaryReportOrderMapper.selectByReportOrderRef(reportOrderRef);
            if(documentaryReportOrder==null){
                LOGGER.error("reqsCancelDocumentaryReportOrder---->documentaryReportOrder is null"+reportOrderRef);
                return;
            }
            AppCustomer appCustomer=appCustomerMapper.selectByPrimaryKey(documentaryReportOrder.getCustomerId());
            //拼接tcp/ip传输数据串 cancelOrder|母账户|子账户|外部请求委托|
            String dataStr="cancelOrder|"+documentaryReportOrder.getInvestorId()+"|"+appCustomer.getCustomerTradeAccount()+"|"+documentaryReportOrder.getOrderRef();
            SocketHandler socketHandler=SocketHandler.getInstance(socketInfo.getTradeHost(),socketInfo.getTradePort());
            boolean flag=socketHandler.isServerClose();
            if(flag){
                throw new Exception("socket连接断开异常");
            }
            LOGGER.info("reqsCancelDocumentaryReportOrder -->:" + dataStr);
            socketHandler.sendMessage(dataStr);
        }catch (Exception e){
            LOGGER.error("reqsCancelDocumentaryReportOrder .....", e);
            //手动回滚数据
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    /**
     * 跟单报单响应
     * @param respData
     * @return
     * @throws Exception
     */
    @Override
    public ResponseEntity<Message> respDocumentaryReportOrder(String respData) throws Exception {
        //交易日|报单提交状态|报单状态|报单编号|报单来源|报单类型|数量|最小成交量|今成交数量|报单价格|本地报单编号|合约|状态信息|报单时间|errid| (接口类型)
        //report|20190218|0|a|2008246|0|0|1|1|0|0.0035|a190218100016|10001703|报单已提交|14:51:52|0|0|0|
        //report|20190219|0|a||0|0|1|1|0|0.0024|a190219100004|10001668|报单已提交|10:16:11|0|4|0|0|
        LOGGER.info("respDocumentaryReportOrder:" + respData);
        String [] respReportData=respData.split("\\|");
        String tradingDay=respReportData[1],submitStatus=respReportData[2],orderStatus=respReportData[3],orderSysId=respReportData[4],orderSource=respReportData[5],
                orderType=respReportData[6],volume=respReportData[7],minVolume=respReportData[8],tradeVolume=respReportData[9],limitPrice=respReportData[10],
                orderRef=respReportData[11],instrumentCode=respReportData[12],statusMsg=respReportData[13],tradeTime=respReportData[14],errorCode=respReportData[15],
                tradeServerCode=respReportData[16];
        //匹配本地报单编号
        DocumentaryReportOrder documentaryReportOrder=documentaryReportOrderMapper.selectByOrderRef(orderRef, instrumentCode);
        if(documentaryReportOrder==null){
            LOGGER.error("c++ 传过来的本地报单编号在库中未找到：" + respData);
            return ResponseEntity.ok(Message.REPORT_ORDER_NOT_EXIST);
        }
        if(StringUtils.isNotBlank(documentaryReportOrder.getOrderStatus()) && ("5".equals(orderStatus) || "0".equals(orderStatus)) &&
                (documentaryReportOrder.getOrderStatus().contains("0") || documentaryReportOrder.getOrderStatus().contains("5"))){
            //报单已存在结束标识
            return ResponseEntity.ok(Message.REPORT_ORDER_END);
        }
        //如果出现错误回报则
        if(!"0".equals(errorCode)){
            /**orderStatus设置为错单**/
            orderStatus="99";
            statusMsg=CTPErrorCode.codeConvertMsg(errorCode);
            /**判断是否是份额不足导致失败**/
            if("9999".equals(errorCode) || "8888".equals(errorCode) || "31".equals(errorCode)){
                /**自动切换交易报单母账户逻辑**/
                switchOptionAccount(errorCode,2);
            }
        }
        documentaryReportOrder.setTradingDay(tradingDay);
        if(StringUtils.isNotBlank(documentaryReportOrder.getOrderStatus())){
            String dbOrderStatus=documentaryReportOrder.getOrderStatus();
            //获取最后一位状态
            String lastStatus=dbOrderStatus.substring(dbOrderStatus.length()-1);
            /**如果出现位置回报顺序错乱的情况**/
            if("0".equals(lastStatus) || "5".equals(lastStatus)){
                /**订单状态重新拼接**/
                String subOrderStatus=dbOrderStatus.substring(0,dbOrderStatus.length()-1);
                String newOrderStatus=subOrderStatus+orderStatus+"|"+lastStatus;
                documentaryReportOrder.setOrderStatus(newOrderStatus);
            }else{
                documentaryReportOrder.setOrderStatus(dbOrderStatus+orderStatus+"|");
            }
        }else{
            documentaryReportOrder.setOrderStatus(orderStatus + "|");
        }
        if(StringUtils.isNotBlank(documentaryReportOrder.getSubmitStatus())){
            String dbOrderStatus=documentaryReportOrder.getOrderStatus();
            String dbSubmitStatus=documentaryReportOrder.getSubmitStatus();
            //获取最后一位状态
            String lastOrderStatus=dbOrderStatus.substring(dbOrderStatus.length()-1);
            String lastSubmitStatus=dbSubmitStatus.substring(dbSubmitStatus.length()-1);
            /**如果出现位置回报顺序错乱的情况**/
            if("0".equals(lastOrderStatus) || "5".equals(lastOrderStatus)){
                /**订单状态重新拼接**/
                String subSubmitStatus=dbSubmitStatus.substring(0,dbSubmitStatus.length()-1);
                String newSubmitStatus=subSubmitStatus+orderStatus+"|"+lastSubmitStatus;
                documentaryReportOrder.setSubmitStatus(newSubmitStatus);
            }else{
                documentaryReportOrder.setSubmitStatus(dbSubmitStatus + submitStatus + "|");
            }
        }else{
            documentaryReportOrder.setSubmitStatus(submitStatus + "|");
        }
        documentaryReportOrder.setOrderSysId(orderSysId);
        documentaryReportOrder.setOrderSource(orderSource);
        documentaryReportOrder.setOrderType(orderType);
        documentaryReportOrder.setMinVolume(Integer.valueOf(minVolume));
        if(documentaryReportOrder.getTradeVolume() == null || documentaryReportOrder.getTradeVolume()<Integer.valueOf(tradeVolume)){
            documentaryReportOrder.setTradeVolume(Integer.valueOf(tradeVolume));
        }
        documentaryReportOrder.setLimitPrice(new BigDecimal(limitPrice));
        documentaryReportOrder.setVolume(Integer.valueOf(volume));
        documentaryReportOrder.setStatusMsg(statusMsg);
        documentaryReportOrder.setReportTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
        documentaryReportOrder.setRespCStatus(1); //得到c++的响应标识
        documentaryReportOrder.setTradeServerCode(tradeServerCode);
        documentaryReportOrderMapper.updateByPrimaryKeySelective(documentaryReportOrder);
        //如果是开仓
        if(!"0".equals(documentaryReportOrder.getOffsetFlag())){
            /**如果是平仓 且订单状态为撤单,错单**/
            if("5".equals(orderStatus) || "99".equals(orderStatus) || ("a".equals(orderStatus) && Constant.QWIN_OPTION_SERVER.equals(tradeServerCode))){
                DocumentaryCloseOrder documentaryCloseOrder=documentaryCloseOrderMapper.selectByReportId(documentaryReportOrder.getReportId());
                //获取撤单手数
                int withdrawNum=documentaryReportOrder.getVolume()-documentaryReportOrder.getTradeVolume();
                //递归将扣除的可用手数加回来
                this.replyEffectiveHoldVolume(documentaryCloseOrder.getOpenOrderRef(), withdrawNum);
            }

        }
        return ResponseEntity.ok(Message.REQ_SUCCESS);
    }

    @Override
    public ResponseEntity<Message> respDocumentaryDealOrder(String respData) throws Exception {
        //开平方向|本地报单编号|合约code|报单编号|成交编号|成交日期|成交时间|手数|价格|买卖类型|交易类型
        //deal|4|180927100195|RM901|      204205|       80029|20180927|14:55:01|1|2484.000|1|0|
        LOGGER.info("respDocumentaryDealOrder:" + respData);
        String [] respDealData=respData.split("\\|");
        String offsetFlag=respDealData[1],orderRef=respDealData[2],instrumentCode=respDealData[3],orderSysId=respDealData[4],
                tradeId=respDealData[5],tradingDay=respDealData[6],tradeTime=respDealData[7],volume=respDealData[8],price=respDealData[9],
                direction=respDealData[10],tradeType=respDealData[11];
        //获取开平标识
        //匹配本地报单编号 orderRef,instrumentCode
        DocumentaryReportOrder documentaryReportOrder=documentaryReportOrderMapper.selectByOrderRef(orderRef, instrumentCode);
        if(documentaryReportOrder==null){
            LOGGER.error("成交--传过来的本地报单编号在库中未找到：" + respData);
            return ResponseEntity.ok(Message.REPORT_ORDER_NOT_EXIST);
        }
        String nowDate=DateUtils.convertDateToString(new Date());
        Object optionSymbolObj=getRedisSymbol(nowDate, instrumentCode, 7);//redisTemplate.opsForHash().get(Constant.OPTION_SYMBOL_INFO + nowDate, instrumentCode);
        OptionSymbol optionSymbol=JSONObject.parseObject(optionSymbolObj.toString(), OptionSymbol.class);
        /**根据商品code查询商品的 最小波动点位等信息**/
        OptionTradingGoods optionTradingGoods=optionTradingGoodsMapper.selectByTradingGoodsCode(optionSymbol.getTargetCode());
        BigDecimal actualVolume=new BigDecimal(volume);
        /**获取开仓时的费率 开仓手续费/张 平仓手续费/张**/
        BigDecimal univalentOpenFee=documentaryReportOrder.getUnivalentOpenFee();
        BigDecimal univalentCloseFee=documentaryReportOrder.getUnivalentCloseFee();
        BigDecimal univalentCostOpenFee=documentaryReportOrder.getUnivalentCostOpenFee();
        BigDecimal univalentCostCloseFee=documentaryReportOrder.getUnivalentCostCloseFee();
        /**开仓回报**/
        if("0".equals(offsetFlag)){
            DocumentaryOpenOrder documentaryOpenOrder=new DocumentaryOpenOrder();
            documentaryOpenOrder.setOpenId(UUIDUtils.getUuid());
            documentaryOpenOrder.setCustomerId(documentaryReportOrder.getCustomerId());
            documentaryOpenOrder.setInvestorId(documentaryReportOrder.getInvestorId());
            documentaryOpenOrder.setProductCode(documentaryReportOrder.getProductCode());
            documentaryOpenOrder.setProductName(documentaryReportOrder.getProductName());
            documentaryOpenOrder.setInstrumentCode(documentaryReportOrder.getInstrumentCode());
            documentaryOpenOrder.setInstrumentName(documentaryReportOrder.getInstrumentName());
            documentaryOpenOrder.setReportId(documentaryReportOrder.getReportId());
            documentaryOpenOrder.setOrderSysId(orderSysId);
            documentaryOpenOrder.setTradeId(tradeId);
            documentaryOpenOrder.setTradingDay(tradingDay);
            documentaryOpenOrder.setTradeTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
            documentaryOpenOrder.setVolume(Integer.valueOf(volume));
            documentaryOpenOrder.setPrice(new BigDecimal(price).setScale(4, BigDecimal.ROUND_HALF_UP));
            documentaryOpenOrder.setOffsetFlag(offsetFlag);
            documentaryOpenOrder.setDirection(direction);
            documentaryOpenOrder.setTradeType(tradeType);
            documentaryOpenOrder.setOrderRef(orderRef);
            documentaryOpenOrder.setStatus(1);
            documentaryOpenOrder.setHoldVolume(Integer.valueOf(volume));
            documentaryOpenOrder.setCreateTime(new Date());
            documentaryOpenOrder.setUpdateTime(new Date());
            documentaryOpenOrder.setStopLossPoint(documentaryReportOrder.getStopLossPoint());
            documentaryOpenOrder.setEffectiveHoldVolume(Integer.valueOf(volume));//可用手数等于成功报单手数
            /**委托单笔开仓费用 总冻结资金/委托手数**/
            BigDecimal reportSinglePrice=documentaryReportOrder.getFrozenDeposit().divide(new BigDecimal(documentaryReportOrder.getVolume()));
            /**实际单笔开仓费用 开仓价*合约乘数**/
            BigDecimal actualSinglePrice=new BigDecimal(price).multiply(optionTradingGoods.getTradingContractMultiplier());
            /**委托撤回资金 (委托单笔开仓费用+开仓手续费)*实际成交手数**/
            BigDecimal reportWithdrawCapital=(reportSinglePrice.add(univalentOpenFee)).multiply(actualVolume);
            /**成交扣除资金 (实际单笔开仓费用+开仓手续费)*实际成交手数**/
            BigDecimal actualWithdrawCapital=(actualSinglePrice.add(univalentOpenFee)).multiply(actualVolume);
            /**开仓使用金额**/
            documentaryOpenOrder.setMargin(actualSinglePrice.multiply(actualVolume));
            documentaryOpenOrder.setOpenFee(univalentOpenFee.multiply(actualVolume));
            //futureOpenOrder.setStopLossAmount(stopLossAmount);
            documentaryOpenOrderMapper.insertSelective(documentaryOpenOrder);
        }else{
            /**平仓回报**/
            DocumentaryCloseOrder documentaryCloseOrder=documentaryCloseOrderMapper.selectByReportId(documentaryReportOrder.getReportId());
            documentaryCloseOrder.setCloseId(UUIDUtils.getUuid());
            documentaryCloseOrder.setCustomerId(documentaryReportOrder.getCustomerId());
            documentaryCloseOrder.setInvestorId(documentaryReportOrder.getInvestorId());
            documentaryCloseOrder.setProductCode(documentaryReportOrder.getProductCode());
            documentaryCloseOrder.setProductName(documentaryReportOrder.getProductName());
            documentaryCloseOrder.setInstrumentCode(documentaryReportOrder.getInstrumentCode());
            documentaryCloseOrder.setInstrumentName(documentaryReportOrder.getInstrumentName());
            documentaryCloseOrder.setOrderSysId(orderSysId);
            documentaryCloseOrder.setTradeId(tradeId);
            documentaryCloseOrder.setTradingDay(tradingDay);
            documentaryCloseOrder.setTradeTime(DateUtils.convertStringToDate("yyyyMMdd HH:mm:ss", tradingDay + " " + tradeTime));
            documentaryCloseOrder.setVolume(Integer.valueOf(volume));
            documentaryCloseOrder.setClosePrice(new BigDecimal(price));

            /**计算平仓手续费**/
            BigDecimal closeFee=univalentCloseFee.multiply(new BigDecimal(volume));
            /**计算开仓手续费**/
            BigDecimal openFee=univalentOpenFee.multiply(new BigDecimal(volume));
            documentaryCloseOrder.setCloseFee(closeFee);
            documentaryCloseOrder.setOpenFee(openFee);
            /**查询平仓对应的开仓数据并修改开仓表中的当前持仓手数**/
            /**获取该笔平仓的开仓报单本地引用**/
            List<DocumentaryOpenOrder> documentaryOpenOrderList=documentaryOpenOrderMapper.selectListByOrderRef(documentaryCloseOrder.getOpenOrderRef());
            Double sumMarketValue=0.00;
            Integer sumVolume=0;
            for(DocumentaryOpenOrder obj:documentaryOpenOrderList){
                //计算市值
                BigDecimal marketValue=obj.getPrice().multiply(new BigDecimal(obj.getVolume()));
                sumMarketValue+=marketValue.doubleValue();
                sumVolume+=obj.getVolume();
            }
            /**计算持仓均价**/
            BigDecimal avgPrice=new BigDecimal(sumMarketValue/sumVolume).setScale(4, BigDecimal.ROUND_HALF_UP);
            /**计算平仓盈亏**/
            BigDecimal closeProfitLoss=null;
            /**平仓权利金=合约乘数*平仓价格*成交手数**/
            BigDecimal closePremium=optionTradingGoods.getTradingContractMultiplier().multiply(new BigDecimal(price)).multiply(actualVolume);
            /**开仓权利金=合约乘数*持仓均价*成交手数**/
            BigDecimal openPremium=optionTradingGoods.getTradingContractMultiplier().multiply(avgPrice).multiply(actualVolume);
            /**平仓盈亏=平仓权利金-开仓权利金**/
            closeProfitLoss=closePremium.subtract(openPremium);
            /**开仓价为持仓均价**/
            documentaryCloseOrder.setOpenPrice(avgPrice);
            documentaryCloseOrder.setCloseProfitLoss(closeProfitLoss);
            documentaryCloseOrder.setDirection(direction);
            documentaryCloseOrder.setTradeType(tradeType);
            documentaryCloseOrder.setOrderRef(orderRef);
            documentaryCloseOrder.setOffsetFlag(offsetFlag);
            documentaryCloseOrder.setCreateTime(new Date());
            documentaryCloseOrder.setUpdateTime(new Date());
            documentaryCloseOrder.setStatus(1);
            documentaryCloseOrder.setClosePremium(closePremium);
            documentaryCloseOrder.setOpenPremium(openPremium);
            //保存来自c++的平仓对象
            documentaryCloseOrderMapper.insertSelective(documentaryCloseOrder);
            this.buckleHoldVolume(documentaryCloseOrder.getOpenOrderRef(), documentaryCloseOrder.getVolume());
        }
        return ResponseEntity.ok(Message.REQ_SUCCESS);
    }

    /**
     * 持仓递归去扣除手数
     * @param orderRef
     * @param closeVolume
     */
    private void buckleHoldVolume(String orderRef,int closeVolume){
        Map<String,Object> map=new HashMap<>();
        map.put("orderRef",orderRef);
        map.put("sort","ASC");
        map.put("type",2);
        DocumentaryOpenOrder documentaryOpenOrder = documentaryOpenOrderMapper.selectByOrderRef(map);
        if(documentaryOpenOrder!=null){
            LOGGER.info("getHoldVolume:" + documentaryOpenOrder.getHoldVolume() + ";closeVolume:" + closeVolume);
            Map<String,Object> mapParameter=new HashMap<String,Object>();
            int difference=documentaryOpenOrder.getHoldVolume()-closeVolume;
            /**差值==0不需要递归 刚好扣完**/
            if(difference<=0){
                mapParameter.put("status", 3);//全部平仓
                //扣除平仓手数
                mapParameter.put("holdVolume", -documentaryOpenOrder.getHoldVolume());
//            mapParameter.put("stopLossAmount",futureOpenOrder.getStopLossAmount().multiply(new BigDecimal(-1)));
//            mapParameter.put("margin", futureOpenOrder.getMargin().multiply(new BigDecimal(-1)));
                mapParameter.put("openId", documentaryOpenOrder.getOpenId());
                documentaryOpenOrderMapper.updateCalculationData(mapParameter);
                if(difference<0){
                    this.buckleHoldVolume(orderRef, Math.abs(difference));
                }
            }
            //部分平仓
            if(difference>0){
                mapParameter.put("status",2);//部分平仓
                //扣除平仓手数
                mapParameter.put("holdVolume", -closeVolume);
//            mapParameter.put("margin", new BigDecimal(cashDeposit).multiply(new BigDecimal(-1)));
                mapParameter.put("openId", documentaryOpenOrder.getOpenId());
                documentaryOpenOrderMapper.updateCalculationData(mapParameter);
            }
        }else{
            LOGGER.error("buckleHoldVolume documentaryOpenOrder is Null -->"+orderRef+"|"+closeVolume);
        }

    }
    /**
     * 递归去扣可用手数
     * @param orderRef
     * @param closeVolume
     */
    public void buckleEffectiveHoldVolume(String orderRef,int closeVolume){
        Map<String,Object> map=new HashMap<>();
        map.put("orderRef",orderRef);
        map.put("sort", "ASC");
        map.put("type", 1);
        DocumentaryOpenOrder documentaryOpenOrder=documentaryOpenOrderMapper.selectByOrderRef(map);
        if(documentaryOpenOrder!=null){
            int difference=documentaryOpenOrder.getEffectiveHoldVolume()-closeVolume;
            if(difference>=0){
                documentaryOpenOrder.setEffectiveHoldVolume(-closeVolume);
                documentaryOpenOrderMapper.updateEffectiveHoldVolume(documentaryOpenOrder);
            }
            if(difference<0){
                documentaryOpenOrder.setEffectiveHoldVolume(-documentaryOpenOrder.getEffectiveHoldVolume());
                documentaryOpenOrderMapper.updateEffectiveHoldVolume(documentaryOpenOrder);
                this.buckleEffectiveHoldVolume(orderRef, Math.abs(difference));

            }
        }else {
            LOGGER.error("buckleEffectiveHoldVolume documentaryOpenOrder is null--->"+orderRef+"|"+closeVolume);
        }

    }
    /**
     * 撤回时 递归去回复刚才扣除的可用手数
     * @param orderRef
     * @param withdrawVolume
     */
    private void replyEffectiveHoldVolume(String orderRef,int withdrawVolume){
        Map<String,Object> map=new HashMap<>();
        map.put("orderRef", orderRef);
        map.put("sort", "DESC");
        map.put("type", 1);
        DocumentaryOpenOrder documentaryOpenOrder=documentaryOpenOrderMapper.selectByOrderRef(map);
        if(documentaryOpenOrder!=null){
            LOGGER.info(orderRef + "replyEffectiveHoldVolume info---->" + documentaryOpenOrder.getHoldVolume() + "|" + documentaryOpenOrder.getEffectiveHoldVolume());
            int difference=documentaryOpenOrder.getHoldVolume()-documentaryOpenOrder.getEffectiveHoldVolume();
            if(difference>=withdrawVolume){
                //加上去 不再递归
                documentaryOpenOrder.setEffectiveHoldVolume(withdrawVolume);
                documentaryOpenOrderMapper.updateEffectiveHoldVolume(documentaryOpenOrder);
            }
            if(difference<withdrawVolume){
                //加上去 继续递归
                documentaryOpenOrder.setEffectiveHoldVolume(difference);
                documentaryOpenOrderMapper.updateEffectiveHoldVolume(documentaryOpenOrder);
                this.replyEffectiveHoldVolume(orderRef, Math.abs(difference - withdrawVolume));
            }
        }else{
            LOGGER.info(orderRef + "replyEffectiveHoldVolume---->futureOpenOrder Is null");
        }
    }

}
